import axios from 'axios'
import { Janus } from './libs/janus_streaming'

const errors = {
  NETWORK_NOT_CONNECTED: { code: 101, msg: '检测到未连接到网络.' },
  ICE_DISCONNECTED: { code: 102, msg: '与ICE服务器断开连接.' },
  SERVER_DISCONNECTED: { code: 103, msg: '与流媒体服务器断开连接.' },
  FUNCTION_ERROR_CALL: { code: 104, msg: '服务器接口调用错误.' },
  PARAMETER_ERROR_SERVER_TYPE: { code: 201, msg: '参数错误.错误的服务器类型.' },
  PARAMETER_ERROR_CHANNEL: { code: 202, msg: '参数错误.错误的通道号.' },
  PARAMETER_ERROR_AUTHORIZATION: { code: 203, msg: '参数错误.未检测到认证码.' },
  PARAMETER_ERROR_SERVER_WS: { code: 204, msg: '参数错误.Websocket参数错误.' },
  PARAMETER_ERROR_SERVER_STREAM: { code: 205, msg: '参数错误.流媒体服务参数错误.' },
  KEEPALIVE_ERROR: { code: 301, msg: '流保活错误.' },
  INIT_PC_ERROR: { code: 302, msg: 'RTCPeerConnect初始化错误.' },
  RECONNECT_ERROR: { code: 303, msg: '重连失败.' }
}

const serverType = {
  MRS: 1,
  SMS: 2
}

/**
 * WebRTC Player for iVMS
 *
 * @author  lis
 * @date    2021-03-19
 */
export class iVMSPlayer {
  constructor({
                vm,
                id,
                serverType,
                server,
                channel,
                authorization,
                transType = 'udp',
                maxReconnectTimes = 6,
                reconnectDuration = 5
              }) {
    this.event = {
      error: error => {
        return error
      },
      ready: data => {
        return data
      },
      play: () => {
      },
      stream: data => {
        return data
      },
      stop: () => {
      },
      disconnect: data => {
        return data
      },
      reconnect: data => {
        return data
      },
      destroy: () => {
      },
      keepalive: () => {
      },
      keepStop: () => {
      }
    }
    // 初始化组件参数
    this.vm = vm
    this.id = id
    this.serverType = serverType
    this.server = server
    this.channel = channel
    this.authorization = authorization
    this.transType = transType
    this.maxReconnectTimes = maxReconnectTimes
    this.reconnectSeconds = reconnectDuration <= 1 ? 1 : (reconnectDuration >= 10 ? 10 : reconnectDuration)

    this.janus = null
    this.pc = null
    this.pcTimer = null

    this.RTCId = null

    // 初始化状态
    this.isPlaying = false
    this.isConnected = false
    this.isReconnecting = false
    this.isReconnected = false

    // 初始化保活参数
    this.timer = null
    this.aliveTime = 60
    this.keepaliveFailCount = 0
    this.sessionId = null

    // 初始化断线重连参数
    this.reconnectTimer = null
    this.reconnectFailCount = 0
    this.timeTemp = 0
  }

  /**
   * 初始化
   * @param container
   * @returns {GBSPlayer}
   */
  init(container) {
    this.container = container
    this.destroy()
    if (this.judgeNetworkStatus()) {
      this.vm.statusLabel = '视频加载中……'
      if (!this.el) {
        this.el = this.bindElement()
      }

      if (this.serverType === serverType.MRS) {
        const { serverIp, serverPort } = this.server.stream
        this.stream_server = `http://${serverIp}:${serverPort}`
        this.mrsConnect()
        if (this.pc) {
          console.log('初始化播放器成功.')
        }
      } else if (this.serverType === serverType.SMS) {
        this.smsParseServer()
        if (this.isConnected) {
          this.destroy()
        }
        this.smsConnect()
      } else {
        this.emit('error', errors.PARAMETER_ERROR_SERVER_TYPE)
      }
      return this
    } else {
      this.emit('error', errors.NETWORK_NOT_CONNECTED)
      return null
    }
  }

  mrsConnect(transcode) {
    if (!transcode) {
      transcode = 0
    }
    if (!window.navigator.onLine) {
      this.emit('error', errors.NETWORK_NOT_CONNECTED)
      return
    }
    if (!this.reconnectTimer) {
      axios.get(`${this.stream_server}/ivms/realplay2?chanpubid=${this.channel}&streamtranstype=6&tanscodemode=${transcode}&AuthorizationId=${this.authorization}`).then(res => {
        if (res.data.SessionId) {
          this.playAddr = res.data.rtcurl
          this.sessionId = res.data.SessionId
          this.isPlaying = true
          this.keepaliveFailCount = 0
          //  视频保活
          this.keepalive()
          this.emit('ready')
          this.mrsPlay()
        } else {
          this.emit('disconnect', errors.FUNCTION_ERROR_CALL)
          this.stop()
        }
      }).catch(() => {
        this.emit('error', errors.FUNCTION_ERROR_CALL)
        this.stop()
      })
    }
  }

  mrsPlay() {
    const _this = this
    const urlObject = this._parse_url(this.playAddr)
    const schema = window.location.protocol
    if (this.pc) {
      this.pc.close()
    }
    // 创建RTC的P2P通道
    // 在Chrome 82及更高版本下，必须增加{ sdpSemantics: 'unified-plan' }参数
    this.pc = new RTCPeerConnection({ sdpSemantics: 'unified-plan' })
    // 监听收流事件
    this.pc.onaddstream = (event) => {
      // $(_this.el).prop('srcObject', event.stream)
      _this.el.srcObject = event.stream
      // 把流暴露给stream事件
      _this.emit('stream', event.stream)
    }
    // 监听ice连接状态
    this.pc.oniceconnectionstatechange = ev => {
      if (this.pc.iceConnectionState === 'disconnected' || this.pc.iceConnectionState === 'failed') {
        this.emit('disconnect', ev)
        this.reconnect()
      } else if (this.pc.iceConnectionState === 'connected') {
        // 连接成功后，重连次数清零
        this.reconnectFailCount = 0
      }
    }
    // 开始握手
    new Promise(function(resolve, reject) {
      _this.pc.addTransceiver('audio', { direction: 'recvonly' })
      _this.pc.addTransceiver('video', { direction: 'recvonly' })

      _this.pc.createOffer(function(offer) {
        resolve(offer)
      }, function(reason) {
        reject(reason)
      })
    }).then(function(offer) {
      return _this.pc.setLocalDescription(offer).then(() => {
        return offer
      }).catch(() => {
        _this.reconnect()
      })
    }).then(function(offer) {
      return new Promise(function(resolve, reject) {
        const port = urlObject.port || 1985

        let api = urlObject.user_query.play || '/rtc/v1/play/'
        if (api.lastIndexOf('/') !== api.length - 1) {
          api += '/'
        }
        let url = schema + '//' + urlObject.server + '' +
          ':' + port + api
        for (const key in urlObject.user_query) {
          if (key !== 'api' && key !== 'play') {
            url += '&' + key + '=' + urlObject.user_query[key]
          }
        }
        url = url.replace(api + '&', api + '?')

        const data = {
          api: url, streamurl: urlObject.url, clientip: null, sdp: offer.sdp
        }
        axios.post(url, JSON.stringify(data)).then(res => {
          // console.log('Got answer: ', res.data)
          resolve(res.data.sdp)
        }).catch(err => {
          reject(err)
        })
      })
    }).then(function(answer) {
      _this.isConnected = true
      return _this.pc.setRemoteDescription(new RTCSessionDescription({ type: 'answer', sdp: answer }))
    }).catch(function(reason) {
      _this.emit('error', errors.INIT_PC_ERROR)
      // throw reason
      _this.reconnect()
    })
    this.emit('play')
  }

  /**
   * 断线重连
   */
  reconnect() {
    if (!this.reconnectTimer) {
      if (this.maxReconnectTimes === 0) {
        this.connectAgain()
      } else if (this.maxReconnectTimes > 0) {
        // 判定重连失败次数是否大于最大重连次数
        if (this.reconnectFailCount >= this.maxReconnectTimes) {
          this.destroy()
          this.emit('disconnect')
          return false
        }
        this.connectAgain()
      } else {
        this.destroy()
        this.emit('disconnect')
        return false
      }
    }
  }

  connectNow() {
    this.reconnectFailCount = 0
    this.emit('reconnect', { count: this.reconnectFailCount, time: 0 })
    this.connectMain()
  }

  connectAgain() {
    this.reconnectFailCount++
    // 停止当前在播视频
    this.stop()
    // 设置本次重连等待时间
    let time = this.reconnectSeconds * this.reconnectFailCount + 1
    this.reconnectTimer = setInterval(() => {
      time--
      // 等待时间结束后执行
      if (time <= 0) {
        clearInterval(this.reconnectTimer)
        this.reconnectTimer = null
        this.sessionId = ''
        this.connectMain()
      }
      this.emit('reconnect', { count: this.reconnectFailCount, time: time })
    }, 1000)
  }

  connectMain() {
    new Promise(resolve => {
      this.stop()
      resolve(this.sessionId)
    }).then(sessionId => {
      if (!sessionId) {
        if (this.serverType === serverType.MRS) {
          this.mrsConnect()
          if (this.pc) {
            console.log('初始化播放器成功.')
          }
        } else if (this.serverType === serverType.SMS) {
          this.smsConnect()
        }
      }
    })
  }

  /**
   * 解析server参数
   * 仅用于服务器类型为SMS时
   */
  smsParseServer() {
    if (this.serverType === serverType.SMS) {
      if (!this.server) {
        return false
      } else {
        if (this.server.ws) {
          const { serverIp, serverPort } = this.server.ws
          this.ws_server = `ws://${serverIp}:${serverPort}`
        } else {
          this.emit('error', errors.PARAMETER_ERROR_SERVER_WS)
          return
        }
        // ICE服务器配置，只有在公网时需要，内网下可不配置
        if (this.server.ice) {
          const { serverIp, serverPort, userName, password } = this.server.ice
          this.ice_server = [{
            urls: `turn:${serverIp}:${serverPort}`,
            userName: userName,
            credential: password
          }]
        }
        if (this.server.stream) {
          const { serverIp, serverPort } = this.server.stream
          this.stream_server = `http://${serverIp}:${serverPort}`
        } else {
          this.emit('error', errors.PARAMETER_ERROR_SERVER_STREAM)
          return
        }
      }
    }
  }

  smsConnect() {
    const self = this
    new Promise(resolve => {
      const janus = new Janus({
        server: this.ws_server,
        iceServer: this.ice_server,
        onlogin: data => {
          self.isConnected = true
          self.janus = janus
          self.RTCId = data.loginId
          self.emit('ready', data)
          resolve()
        },
        onremotestream: stream => {
          const session = Janus.sessions[self.janus.getsessionId()]
          session.attachMediaStream(self.el, stream)
          self.emit('stream', stream)
        },
        // ICE状态监听
        // 相当于RTCPeerConnection.oniceconnectionstatechange监听
        // 返回ICE连接状态
        iceState: state => {
          // 此处默认不做处理，在error中做处理
        },
        error: async err => {
          self.emit('error', err)
          self.reconnect()
        }
      })
    }).then(() => {
      self.smsPlay()
    })
  }

  smsPlay() {
    if (!this.isConnected) {
      this.emit('error', errors.SERVER_DISCONNECTED)
      return
    }
    if (this.isPlaying) {
      this.stop()
    }
    const self = this
    new Promise((resolve, reject) => {
      axios.get(`${this.stream_server}/ivms/realplay2?chanpubid=${this.channel}&streamtranstype=4&webrtcid=${this.RTCId}&AuthorizationId=${this.authorization}`).then(res => {
        resolve(res)
      }).catch(err => {
        reject(err)
      })
    }).then(resp => {
      self.sessionId = resp.data.SessionId
      document.getElementById('video-' + self.id).setAttribute('session-id', self.sessionId)
      self.isPlaying = true
      self.keepalive()
      self.pc = self.janus.gethandle().webrtcStuff.pc
      if (!self.pc) {
        self.pcKeepalive()
      }
      self.emit('play')
    }).catch(err => {
      self.emit('error', errors.FUNCTION_ERROR_CALL)
      self.reconnect()
    })
  }

  // pc保活
  pcKeepalive() {
    const self = this
    this.pcTimer = setInterval(() => {
      self.pc = self.janus.gethandle().webrtcStuff.pc
      if (self.pc) {
        clearInterval(self.pcTimer)
      }
    }, 100)
  }

  /**
   * 判定当前网络状态
   */
  judgeNetworkStatus() {
    return !!window.navigator.onLine
  }

  transcode(val) {
    this.destroy()
    setTimeout(() => {
      if (this.judgeNetworkStatus()) {
        // this.el = this.bindElement ()
        this.mrsConnect(val)
        if (this.pc) {
          console.log('初始化播放器成功.')
        }
      } else {
        this.emit('error', errors.NETWORK_NOT_CONNECTED)
      }
    }, 1500)
  }

  /**
   * 视频流保活
   */
  keepalive() {
    if (!this.vm.$refs[this.id]) {
      this.stop()
      this.emit('keepStop')
      return false
    }
    if (!this.isPlaying) return
    if (this.timer) {
      clearTimeout(this.timer)
      this.timer = null
    }

    if (this.sessionId && !this.reconnectTimer) {
      this.timer = setTimeout(() => {
        axios.get(`${this.stream_server}/ivms/video_keeplive?SessionId=${this.sessionId}&AuthorizationId=${this.authorization}`).then(resp => {
          if (resp.data.errcode === 0) {
            this.keepaliveFailCount = 0
            this.aliveTime = resp.data.Keeplive
            this.keepalive()
          } else {
            if (this.keepaliveFailCount > 3) {
              this.stop()
              return
            } else {
              this.keepalive()
            }
            this.keepaliveFailCount++
          }
        }).catch(() => {
          this.emit('error', errors.KEEPALIVE_ERROR)
          if (this.keepaliveFailCount > 3) {
            this.stop()
            return
          } else {
            this.keepalive()
          }
          this.keepaliveFailCount++
        })
      }, this.aliveTime * 1000 / 2)
    }
  }

  /**
   * 停止播放
   */
  stop() {
    if (!this.isConnected) {
      return false
    }
    if (!this.isPlaying) {
      return false
    }
    this.isPlaying = false
    this.isConnected = false

    if (this.sessionId) {
      axios.get(`${this.stream_server}/ivms/close_realplay?SessionId=${this.sessionId}&AuthorizationId=${this.authorization}`).then(() => {
      })
    }
    if (this.serverType === serverType.MRS) {
      if (this.pc) {
        this.pc.close()
      }
      this.pc = null
    } else {
      if (this.janus) {
        this.janus.destroySession()
        // this.janus.destroyWebsocket()
      }
      this.janus = null
    }
    this.destroy()
    this.emit('stop')
  }

  /**
   * 销毁组件
   * 默认不销毁DOM中的video节点
   */
  destroy(isDestroyEl) {
    // 停止播放
    if (this.isPlaying || this.isConnected) {
      this.stop()
    }
    // 关闭保活定时器
    clearTimeout(this.timer)
    this.timer = null
    // 关闭重连定时器
    clearInterval(this.reconnectTimer)
    this.reconnectTimer = null
    clearInterval(this.pcTimer)
    this.pc = null
    this.sessionId = ''
    this.isConnected = false
    this.isPlaying = false
    this.aliveTime = 60
    this.sessionId = null
    if (isDestroyEl) {
      this.destroyEl()
    }
    this.emit('destroy')
  }

  destroyEl() {
    if (this.el) {
      this.container.removeItem(this.el)
      // document.getElementById(this.container).removeChild(this.el)
      // this.el.remove()
    }
  }

  /**
   * 绑定Video节点
   * @returns {HTMLVideoElement}
   */
  bindElement() {
    const videoElement = document.createElement('video')
    videoElement.setAttribute('autoplay', true)
    videoElement.setAttribute('id', 'video-' + this.id)
    videoElement.setAttribute('bind-id', this.channel)
    videoElement.style.width = '100%'
    videoElement.style.height = '100%'
    videoElement.style.objectFit = 'contain'
    videoElement.style.flex = '1'
    videoElement.style.background = 'black'
    // 此处必须设置静音，否则无法自动播放
    videoElement.setAttribute('muted', true)
    if (!this.container.innerHTML) {
      this.container.appendChild(videoElement)
    } else {
      new Promise(resolve => {
        this.container.innerHTML = ''
        resolve(videoElement)
      }).then(videoElement => {
        this.container.appendChild(videoElement)
      })
    }
    return videoElement
  }

  /**
   * 获取ICE状态
   * @returns {string|RTCIceConnectionState}
   */
  getIceState() {
    return this.pc.iceConnectionState
  }

  /**
   * 获取本地网络连接
   * @returns {boolean}
   */
  getLocalNetWorkState() {
    return this.judgeNetworkStatus()
  }

  /**
   * 静音
   */
  mute() {
    this.el.mute = true
  }

  /**
   * 取消静音
   */
  unMute() {
    this.el.mute = false
  }

  on(event, callback) {
    this.event[event] = callback
  }

  emit(event, data) {
    try {
      this.event[event].call(null, data)
    } catch (e) {
      console.error(e)
    }
  }

  _parse_url(url) {
    const a = document.createElement('a')
    a.href = url.replace('webrtc://', 'http://').replace('rtc://', 'http://')

    let vhost = a.hostname
    let app = a.pathname.substr(1, a.pathname.lastIndexOf('/') - 1)
    const stream = a.pathname.substr(a.pathname.lastIndexOf('/') + 1)

    app = app.replace('...vhost...', '?vhost=')
    if (app.indexOf('?') >= 0) {
      const params = app.substr(app.indexOf('?'))
      app = app.substr(0, app.indexOf('?'))

      if (params.indexOf('vhost=') > 0) {
        vhost = params.substr(params.indexOf('vhost=') + 'vhost='.length)
        if (vhost.indexOf('&') > 0) {
          vhost = vhost.substr(0, vhost.indexOf('&'))
        }
      }
    }

    if (a.hostname === vhost) {
      const re = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/
      if (re.test(a.hostname)) {
        vhost = '__defaultVhost__'
      }
    }

    let schema = 'rtmp'
    if (url.indexOf('://') > 0) {
      schema = url.substr(0, url.indexOf('://'))
    }

    let port = a.port
    if (!port) {
      if (schema === 'webrtc' || schema === 'rtc') {
        port = 1985
      }
    }

    const ret = {
      url: url,
      schema: schema,
      server: a.hostname,
      port: port,
      vhost: vhost,
      app: app,
      stream: stream
    }
    this._fill_query(a.search, ret)

    return ret
  }

  _fill_query(query_string, obj) {
    obj.user_query = {}
    if (query_string.length === 0) {
      return
    }
    if (query_string.indexOf('?') >= 0) {
      query_string = query_string.split('?')[1]
    }
    const queries = query_string.split('&')
    for (let i = 0; i < queries.length; i++) {
      const elem = queries[i]

      const query = elem.split('=')
      obj[query[0]] = query[1]
      obj.user_query[query[0]] = query[1]
    }
    if (obj.domain) {
      obj.vhost = obj.domain
    }
  }
}
